//@author A0099619J
#include "Parser.h"

const std::string Parser::monthsShort[12] = {"jan", "feb", "mar", "apr", "may", "jun", "jul", "aug", "sep", "oct", "nov", "dec"};
const std::string Parser::months[12] = {"january", "february", "march", "april", "may", "june",
									"july", "august", "september", "october", "november", "december"};

const std::string Parser::daysShort[7] = {"sun", "mon", "tue", "wed", "thu", "fri", "sat"};
const std::string Parser::days[7] = {"sunday", "monday", "tuesday", "wednesday", "thursday", "friday", "saturday"};

const std::string Parser::TODAY = "today";
const std::string Parser::TOMORROW = "tomorrow";

const std::string Parser::AM = "am";
const std::string Parser::PM = "pm";

// Match a date in the following formats:
// 11 November 2013, 9 Oct 14 (full or short months and years)
const regex Parser::dateFormat("([0-9]{1,2})\\s*([a-zA-Z]+)\\s*([0-9]{0,4})");

// Match a date in the following formats:
// DD-MM-YY, DD/MM/YY, where DD, MM can be 1 or 2 digits, YY can be 2 or 4 digits
const regex Parser::condensedDateFormat("([0-9]{1,2})[\\s\\-\\/]([0-9]{1,2})[\\s\\-\\/](([0-9]{2}){0,2})");

// Match a date in the following format:
// DDMMYY, DDMMYYYY
const regex Parser::integerDateFormat("([0-9]{2})([0-9]{2})(([0-9]{2}){1,2})");

// Match a time in the following formats:
// 11:11, 8.08, 2359, 9AM, 12:00 PM
const regex Parser::timeFormat("([0-9]{1,2})[:.]?([0-9]{0,2})\\s*([a-zA-Z]*)");

void Parser::splitString(std::string str, vector<std::string> &result)
{
	trimSpaces(str);
	std::string nextArgument;
	int startIndex = 0;
	int endIndex = str.find(" ");
	int length;
	
	nextArgument = str.substr(startIndex, endIndex);
	result.push_back(nextArgument);
	if (endIndex != string::npos) {
		do {
			startIndex = endIndex + 1;
			endIndex = str.find(",", startIndex);
			length = endIndex - startIndex;
			nextArgument = str.substr(startIndex, length);
			trimSpaces(nextArgument);
			result.push_back(nextArgument);
		} while (endIndex != std::string::npos);
	}
}

void Parser::trimSpaces(std::string &str) {
	str.erase(0, str.find_first_not_of(" \t"));

	if (str.find_last_not_of(" \t") != std::string::npos)
		str.erase(str.find_last_not_of(" \t")+1);
}

std::string Parser::toLower(const std::string &str) {
	std::string lowerStr = str;
	for (unsigned int i=0; i<str.length(); i++) {
		lowerStr[i] = tolower(str[i]);
	}
	return lowerStr;
}

CommandType Parser::parseCommandType(std::string command) {
	command = toLower(command);

	CommandType type;
	if (command == "add" || command == "create" || command == "new") {
		type = ADD;
	}
	else if (command == "delete" || command == "remove") {
		type = DELETE;
	}
	else if (command == "display" || command == "show") {
		type = DISPLAY;
	}
	else if (command == "modify" || command == "update" || command=="edit") {
		type = UPDATE;
	}
	else if (command == "search" || command == "find") {
		type = SEARCH;
	}
	else if (command == "mark" || command == "done") {
		type = MARK;
	}
	else if (command == "unmark" || command == "undone") {
		type = UNMARK;
	}
	else if (command == "undo" || command == "revert") {
		type = UNDO;
	}
	else if (command == "redo") {
		type = REDO;
	}
	else if(command == "summary") {
		type = SUMMARY;
	}
	else if (command == "exit" || command == "quit") {
		type = EXIT;
	}
	else {
		type = INVALID;
	}

	return type;
}

bool Parser::parseTaskTypeAndIndex(std::string str, TaskType &type, int &index) {
	if (str.length() > 1 && isPositiveInteger(str.substr(1)))
	{
		string indexStr = str.substr(1);
		string typeStr = str.substr(0,1);
		typeStr = Parser::toLower(typeStr);

		if (isPositiveInteger(indexStr)) {
			index = stoi(indexStr) - 1;
		}
		else {
			return false;
		}

		if (typeStr == "t")
			type = TIMED;
		else if (typeStr == "d")
			type = DEADLINE;
		else if (typeStr == "f")
			type = FLOATING;
		else {
			return false;
		}
		return true;
	}
	else
		return false;
}
//@author A0105566E
bool Parser::isPositiveInteger(const std::string & s)
{
	if(s.empty()) return false ;

	char * p ;
	strtol(s.c_str(), &p, 10) ;

	if (*p == 0 && stoi(s) != 0)
		return true;
	else
		return false;
}
//@author A0099619J
// Creates a Date object from user input
Date Parser::parseDate(std::string str) {

	Date date;
    std::smatch match;
	int result = ERROR_PROCESSING_COMMAND;
	if (matchDay(str, date) || matchDate(str, date)) {
		result = SUCCESS;
	}

	if (result == SUCCESS && date.isValidDate()) {
		log(str + " converted to " + date.formatDate());
		return date;
	}
	else {
		return NULL_DATE;
	}
}

bool Parser::matchDay(std::string str, Date& date) {
	std::smatch match;

	if (std::regex_match(str, match, regex("([A-Za-z]*)"))) {
		string day = toLower(match[1]);

		if (day == TODAY) {
			date = Date::getCurrentDate();
			return true;
		}
		else if (day == TOMORROW) {
			date = Date::getCurrentDate() + 1;
			return true;
		}

		int dayOfWeek = -1;
		for (int i=0; i<7; i++) {
			if (day == daysShort[i] || day == days[i]) {
				dayOfWeek = i;
				break;
			}
		}
		if (dayOfWeek == -1) {
			return false;
		}

		int today = Date::getCurrentDate().dayOfWeek();
		int diff = dayOfWeek - today;
		if (diff <= 0) {
			diff += 7;
		}
		date = Date::getCurrentDate() + diff;
		return true;
	}
	return false;
}

bool Parser::matchDate(std::string str, Date& date) {
	int DD = 0, MM = 0, YY = 0;
	std::smatch match;
	bool success = false;

	if (std::regex_match(str, match, dateFormat)) {
		std::string month = toLower(match[2]);

		for (int i=0; i<12; i++) {
			if (month == monthsShort[i] || month == months[i]) {
				MM = i+1;
				break;
			}
		}
		if (MM != 0) {
			success = true;
			DD = stoi(match[1]);
			YY = match[3] == "" ? Date::getCurrentDate().getYear() : stoi(match[3]);
			YY %= 100;
		}
	}
	else if (std::regex_match(str, match, condensedDateFormat) ||
			 std::regex_match(str, match, integerDateFormat)) {
		DD = stoi(match[1]);
		MM = stoi(match[2]);
		YY = match[3] == "" ? Date::getCurrentDate().getYear() : stoi(match[3]);
		YY %= 100;
		success = true;
	}

	if (success) {
		date = Date(DD, MM, YY);
	}
	return success;
}

// Creates a Time object from user input
Time Parser::parseTime(std::string str) {
	int HH = 0;
	int MM = 0;
	Time time;

    std::smatch match;

    if (std::regex_match(str, match, timeFormat)) {
		HH = stoi(match[1]);
		MM = match[2] == "" ? 0 : stoi(match[2]);

		if (HH < 12 && toLower(match[3]) == PM) {
			HH += 12;
		}
	}
	else {
		return NULL_TIME;
	}

	time = Time(HH, MM);
	if (time.isValidTime()) {
		log(str + " converted to " + std::to_string(HH) + std::to_string(MM));
		return time;
	}
	else {
		return NULL_TIME;
	}
}